package com.ruyuan2020.furnishing.trade.service.impl;

import com.ruyuan2020.common.domain.BasePage;
import com.ruyuan2020.common.exception.BusinessException;
import com.ruyuan2020.common.util.BeanCopierUtils;
import com.ruyuan2020.furnishing.account.api.AccountApi;
import com.ruyuan2020.furnishing.account.domain.GoldOperationRequestDTO;
import com.ruyuan2020.furnishing.payment.api.PaymentApi;
import com.ruyuan2020.furnishing.payment.domain.PaymentDTO;
import com.ruyuan2020.furnishing.tender.api.TenderApi;
import com.ruyuan2020.furnishing.tender.domain.CheckTrustRequestDTO;
import com.ruyuan2020.furnishing.trade.constant.ConfigConstants;
import com.ruyuan2020.furnishing.trade.constant.TradeConstants;
import com.ruyuan2020.furnishing.trade.constant.TradeStatusConstants;
import com.ruyuan2020.furnishing.trade.dao.TradeLogDAO;
import com.ruyuan2020.furnishing.trade.domain.TradeLogDTO;
import com.ruyuan2020.furnishing.trade.domian.TradeDTO;
import com.ruyuan2020.furnishing.trade.domian.TradeLogDO;
import com.ruyuan2020.furnishing.trade.domian.TradeQuery;
import com.ruyuan2020.furnishing.trade.service.TradeService;
import com.ruyuan2020.furnishing.trade.util.TradeNoGenerator;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Objects;

@Service
public class TradeServiceImpl implements TradeService {

    @Autowired
    private TradeLogDAO tradeLogDAO;

    @DubboReference(version = "1.0.0", cluster = "failfast", timeout = 3000)
    private PaymentApi paymentApi;

    @DubboReference(version = "1.0.0", cluster = "failfast", timeout = 3000)
    private AccountApi accountApi;

    @DubboReference(version = "1.0.0", cluster = "failfast", timeout = 3000)
    private TenderApi tenderApi;


    @Autowired
    private TradeNoGenerator tradeNoGenerator;

    /**
     * 获取交易记录
     *
     * @param tradeNo 交易流水号
     * @return 交易记录
     */
    @Override
    public TradeLogDTO getTradeLog(String tradeNo) {
        TradeLogDO tradeLogDO = tradeLogDAO.getByTradeNo(tradeNo);
        return tradeLogDO.clone(TradeLogDTO.class);
    }

    /**
     * 充值金币
     *
     * @param tradeDTO 交易信息
     * @return 支付URL
     */
    @Override
    @Transactional
    public String rechargeGood(Long memberId, TradeDTO tradeDTO) {
        // 充值信息检查
        if (ConfigConstants.minPay.compareTo(tradeDTO.getAmount()) > 0) {
            throw new BusinessException("充值金额不能小于" + ConfigConstants.minPay);
        }
        saveLog(tradeDTO, TradeConstants.PAYMENT_TYPE_RECHARGE);
        return buildPayUrl(tradeDTO);
    }

    /**
     * 支付托管金
     *
     * @param tradeDTO 交易信息
     * @return 支付URL
     */
    @Override
    @Transactional
    public String payTrust(TradeDTO tradeDTO) {
        // 是否使用金币支付
        boolean isGold = Objects.nonNull(tradeDTO.getGold()) && tradeDTO.getGold().compareTo(BigDecimal.ZERO) > 0;
        // 是否在线支付
        boolean isOnline = Objects.nonNull(tradeDTO.getAmount()) && tradeDTO.getAmount().compareTo(BigDecimal.ZERO) > 0;
        checkPayTrust(tradeDTO, isGold, isOnline);
        saveLog(tradeDTO, TradeConstants.PAYMENT_TYPE_TRUST);
        if (isGold && isOnline) { // 使用金币抵扣一部分，剩下在线支付
            // 如果用金币混合支付，就冻结金币
            GoldOperationRequestDTO goldOperationRequestDTO = new GoldOperationRequestDTO();
            goldOperationRequestDTO.setNumber(tradeDTO.getGold());
            goldOperationRequestDTO.setMemberId(tradeDTO.getMemberId());
            accountApi.freezeGold(goldOperationRequestDTO);
        } else if (isGold) { // 只使用金币支付
            GoldOperationRequestDTO goldOperationRequestDTO = new GoldOperationRequestDTO();
            goldOperationRequestDTO.setMemberId(tradeDTO.getMemberId());
            goldOperationRequestDTO.setNumber(tradeDTO.getGold());
            goldOperationRequestDTO.setLog("支付保证金");
            // 如果只用金币支付，就扣减金币
            accountApi.payGold(goldOperationRequestDTO);
            this.informTradeCompletedEvent(tradeDTO.getTradeNo());
        }
        if (isOnline) {
            return buildPayUrl(tradeDTO);
        }
        return "";
    }

    /**
     * 检查支付托管金信息是否正确
     *
     * @param tradeDTO 交易信息
     * @param isGold   是否金币支付
     * @param isOnline 是否在线支付
     */
    private void checkPayTrust(TradeDTO tradeDTO, boolean isGold, boolean isOnline) {
        // 创建检查托管金请求
        CheckTrustRequestDTO checkTrustRequestDTO = new CheckTrustRequestDTO();
        checkTrustRequestDTO.setMemberId(tradeDTO.getMemberId());
        checkTrustRequestDTO.setTenderId(tradeDTO.getTenderId());

        if (isGold && isOnline) { // 使用金币抵扣一部分，剩下在线支付
            checkTrustRequestDTO.setAmount(tradeDTO.getGold().add(tradeDTO.getAmount()));
        } else if (isGold) { // 只使用金币支付
            checkTrustRequestDTO.setAmount(tradeDTO.getGold());
        } else if (isOnline) { // 只使用在线支付
            checkTrustRequestDTO.setAmount(tradeDTO.getAmount());
        } else throw new BusinessException("支付信息不正确");
        // 检查支付托管金信息是否正确
        tenderApi.checkPayTrust(checkTrustRequestDTO);
    }

    /**
     * 完成交易
     *
     * @param tradeNo 交易id
     */
    @Override
    @Transactional
    public void informTradeCompletedEvent(String tradeNo) {
        TradeLogDO tradeLogDO = tradeLogDAO.getByTradeNo(tradeNo);
        if (!TradeStatusConstants.STATUS_FINISHED.equals(tradeLogDO.getStatus())) {
            tradeLogDO.setStatus(TradeStatusConstants.STATUS_FINISHED);
            tradeLogDAO.update(tradeLogDO);
            if (TradeConstants.PAYMENT_TYPE_RECHARGE.equals(tradeLogDO.getPaymentType())) {
                // 处理充值金币的逻辑
                handleRecharge(tradeLogDO);
            } else if (TradeConstants.PAYMENT_TYPE_TRUST.equals(tradeLogDO.getPaymentType())) {
                // 处理支付保证金的逻辑
                handlePayTrust(tradeLogDO);
            }
        }
    }

    @Override
    public BasePage<TradeDTO> listByPage(TradeQuery tradeQuery) {
        return BeanCopierUtils.convert(tradeLogDAO.listPage(tradeQuery), TradeDTO.class);
    }

    @Override
    public TradeDTO get(Long id) {
        return tradeLogDAO.getById(id).map(it -> it.clone(TradeDTO.class)).orElseThrow(() -> new BusinessException("交易信息不存在"));
    }

    /**
     * 调用支付模块，构建支付URL
     *
     * @param tradeDTO 交易信息
     * @return 支付URL
     */
    private String buildPayUrl(TradeDTO tradeDTO) {
        // 构建支付url
        PaymentDTO paymentDTO = new PaymentDTO();
        paymentDTO.setMemberId(tradeDTO.getMemberId());
        paymentDTO.setPaymentMethod(tradeDTO.getPaymentMethod());
        paymentDTO.setAmount(tradeDTO.getAmount());
        paymentDTO.setTradeNo(tradeDTO.getTradeNo());
        return paymentApi.buildPayUrl(paymentDTO);
    }

    /**
     * 保存交易记录
     *
     * @param tradeDTO    交易信息
     * @param paymentType 支付类型
     */
    private void saveLog(TradeDTO tradeDTO, String paymentType) {
        String tradeNo = tradeNoGenerator.generate();
        // 保存交易记录
        TradeLogDO tradeLogDO = new TradeLogDO();
        tradeLogDO.setMemberId(tradeDTO.getMemberId());
        tradeLogDO.setAmount(tradeDTO.getAmount());
        tradeLogDO.setGold(tradeDTO.getGold());
        tradeLogDO.setPaymentType(paymentType);
        tradeLogDO.setPaymentMethod(tradeDTO.getPaymentMethod());
        tradeLogDO.setTradeNo(tradeNo);
        tradeLogDO.setTenderId(tradeDTO.getTenderId());
        tradeLogDO.setStatus(TradeStatusConstants.STATUS_IN_TRADING);
        tradeLogDAO.save(tradeLogDO);
        tradeDTO.setTradeNo(tradeNo);
    }

    /**
     * 处理充值金币的逻辑
     *
     * @param tradeLogDO 交易记录
     */
    private void handleRecharge(TradeLogDO tradeLogDO) {
        GoldOperationRequestDTO goldOperationRequestDTO = new GoldOperationRequestDTO();
        goldOperationRequestDTO.setNumber(tradeLogDO.getAmount());
        goldOperationRequestDTO.setMemberId(tradeLogDO.getMemberId());
        goldOperationRequestDTO.setLog("充值金币");
        accountApi.addGold(goldOperationRequestDTO);
    }

    /**
     * 处理支付托管金的逻辑
     *
     * @param tradeLogDO 交易记录
     */
    private void handlePayTrust(TradeLogDO tradeLogDO) {
        if (Objects.nonNull(tradeLogDO.getGold()) && tradeLogDO.getGold().compareTo(BigDecimal.ZERO) > 0) {
            GoldOperationRequestDTO goldOperationRequestDTO = new GoldOperationRequestDTO();
            goldOperationRequestDTO.setNumber(tradeLogDO.getGold());
            goldOperationRequestDTO.setMemberId(tradeLogDO.getMemberId());
            goldOperationRequestDTO.setLog("支付托管金");
            accountApi.payFreezeGold(goldOperationRequestDTO);
        }
        tenderApi.informPayTrustCompletedEvent(tradeLogDO.getTenderId());
    }
}
